/* 
 * Copyright 2011 kOSMik.
 * This file is part of kOSMik.
 * 
 * kOSMik is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * kOSMik is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with kOSMik.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * kOSMik:
 * Open Street Map - Editor.
 * http://code.google.com/p/swp-dv-ws2010-osm-2/
 */

package de.fuberlin.kosmik.gui.view.adapter;

import java.util.Collection;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import de.fuberlin.kosmik.R;
import de.fuberlin.kosmik.data.osmt.OsmtData;
import de.fuberlin.kosmik.data.osmt.OsmtDataFactory;
import de.fuberlin.kosmik.data.osmt.OsmtElementRecord;
import de.fuberlin.kosmik.data.osmt.OsmtFeatureRecord;
import de.fuberlin.kosmik.data.osmt.OsmtNodeRecord;
import de.fuberlin.kosmik.gui.activity.TagSearchActivity;
import de.fuberlin.kosmik.gui.view.adapter.TagSearchAdapter.TagListItem;
import de.fuberlin.kosmik.gui.view.adapter.TagSearchAdapter.ViewHeaderHolder;
import de.fuberlin.kosmik.gui.view.adapter.TagSearchAdapter.ViewItemHolder;
import de.fuberlin.kosmik.log.Constants;

/**
 * A Adapter which can receive tag queries and shows the results.
 * 
 * @author micha
 * 
 */
public class TagSearchAdapter extends
		ListWithHeadersAdapter<TagListItem, ViewHeaderHolder, ViewItemHolder> {

	/**
	 * Indicates of what type the list item is.
	 * 
	 * @author micha
	 * 
	 */
	public static enum ItemType {
		/**
		 * Item is a feature.
		 */
		FEATURE_ITEM,
		/**
		 * Item is a bundle.
		 */
		BUNDLE_ITEM,
		/**
		 * Item is a theme.
		 */
		THEME_ITEM
	}

	/**
	 * Title of the feature list.
	 */
	String featureTitle;
	/**
	 * Title of the bundle list.
	 */
	String bundleTitle;
	/**
	 * Title of the theme list.
	 */
	String themeTitle;
	/**
	 * OsmtData object which fetches the results from the db.
	 */
	OsmtData osmtData;
	/**
	 * Is true if the feature table should be queried.
	 */
	boolean filterFeatures;
	/**
	 * Is true if the bundle table should be queried.
	 */
	boolean filterBundles;
	/**
	 * Is true if the theme table should be queried.
	 */
	boolean filterThemes;
	/**
	 * Indicates which elements should be shown.
	 */
	final int elementFilter;
	private Context parent;

	private GetItemsTask asyncTask;

	/**
	 * Constructor inits the LayoutInflator and creates a osmtData object.
	 * 
	 * @param context of the caller activity
	 * @param tableFilter indicates which tables should be queried.
	 * @param elementFilter indicates which elements should be shown.
	 */
	public TagSearchAdapter(Context context, byte tableFilter, int elementFilter) {
		super(context, R.layout.track_list_header, R.layout.tag_search_row);
		Log.d(Constants.TAG, "elementFilter:" + elementFilter);
		this.elementFilter = elementFilter;
		setTableFilterMode(tableFilter);
		parent = context;
		featureTitle = parent.getString(R.string.tagsearch__feature);
		bundleTitle = parent.getString(R.string.tagsearch__bundle);
		themeTitle = parent.getString(R.string.tagsearch__theme);
		osmtData = OsmtDataFactory.createOsmtData(parent);
	}

	private void setTableFilterMode(byte tableFilter) {
		switch (tableFilter) {
		case TagSearchActivity.TABLE_FILTER_FEATURES:
			filterFeatures = true;
			break;
		case TagSearchActivity.TABLE_FILTER_BUNDLES:
			filterBundles = true;
			break;
		case TagSearchActivity.TABLE_FILTER_THEMES:
			filterThemes = true;
			break;
		default: // TABLE_FILTER_ALL
			filterFeatures = true;
			filterBundles = true;
			filterThemes = true;
		}
	}

	@Override
	ViewHeaderHolder findViewsOfHeader(View convView) {
		ViewHeaderHolder headerHolder = new ViewHeaderHolder();
		headerHolder.headerName = (TextView) convView.findViewById(R.id.track_list_header_name);
		return headerHolder;
	}

	@Override
	void setViewsOfHeader(ViewHeaderHolder headerHolder, TagListItem listItem) {
		headerHolder.headerName.setText(listItem.headerName);
	}

	@Override
	ViewItemHolder findViewsOfItem(View convView) {
		ViewItemHolder itemHolder = new ViewItemHolder();
		itemHolder.name = (TextView) convView.findViewById(R.id.tag_search_name);
		itemHolder.comment = (TextView) convView.findViewById(R.id.tag_search_comment);
		return itemHolder;
	}

	@Override
	void setViewsOfItem(ViewItemHolder itemHolder, TagListItem listItem) {
		itemHolder.name.setText(listItem.osmtElement.toString());
		itemHolder.comment.setText(listItem.osmtElement.comment);
		itemHolder.listItem = listItem;
	}

	/**
	 * Starts new GetItemsTask with the given query.
	 * 
	 * @param query search word.
	 */
	public void searchAndUpdateAdapter(String query) {
		// clear all items before adding new items
		this.clear();
		if (asyncTask != null && !asyncTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
			asyncTask.cancel(true);
			Log.d(Constants.TAG, "kill asyncTask");
		}
		asyncTask = new GetItemsTask();
		asyncTask.execute(query);
	}

	/**
	 * A ViewHolderClass which contains all Views of a list item to avoid
	 * findViewById() calls.
	 * 
	 */
	public static class ViewItemHolder implements ViewHolder<TagListItem> {
		/**
		 * Contains all info about the item.
		 */
		TagListItem listItem;
		/**
		 * View which shows the name of the current item.
		 */
		public TextView name;
		/**
		 * View which shows the first two lines of the item's comment.
		 */
		public TextView comment;
		/**
		 * View which shows the icon of the current item.
		 */
		public ImageView icon;

		@Override
		public boolean isHeader() {
			return false;
		}

		@Override
		public TagListItem getItem() {
			return listItem;
		}
	}

	/**
	 * A ViewHolderClass which contains all Views of a list header to avoid
	 * findViewById() calls.
	 */
	public static class ViewHeaderHolder implements ViewHolder<TagListItem> {
		/**
		 * View which shows the name of the current header.
		 */
		TextView headerName;

		@Override
		public boolean isHeader() {
			return true;
		}

		@Override
		public TagListItem getItem() {
			return null;
		}
	}

	/**
	 * Contains basic information about a track item.
	 */
	public class TagListItem implements ListItem {
		/**
		 * Indicates whether the item is a header or a standard item.
		 */
		private boolean isHeader;
		/**
		 * Name of the header.
		 */
		String headerName;
		/**
		 * Contains information about the osmtItem.
		 */
		public OsmtElementRecord osmtElement;
		/**
		 * Type is FEATURE_ITEM, THEME_ITEM or BUNDLE_ITEM.
		 */
		public ItemType item_type;

		/**
		 * Constructor, should used for ListItems.
		 * 
		 * @param osmtElmnt object of the item
		 * @param type of the given osmtElmnt
		 */
		TagListItem(OsmtElementRecord osmtElmnt, ItemType type) {
			osmtElement = osmtElmnt;
			item_type = type;
		}

		/**
		 * Constructor, should used for header items.
		 * 
		 * @param headerName Name of the Header.
		 */
		TagListItem(String headerName) {
			this.isHeader = true;
			this.headerName = headerName;
		}

		@Override
		public boolean isHeader() {
			return isHeader;
		}
	}

	// method of the TextWatcher interface.

	@Override
	public void onTextChanged(CharSequence s, int start, int before, int count) {
		this.searchAndUpdateAdapter(s.toString());
	}

	/**
	 * GetItemsTask is a AsyncTask which fetches all results of the given query
	 * in the background and add these to the adapter.
	 * 
	 * @author micha
	 * 
	 */
	class GetItemsTask extends AsyncTask<String, Void, OsmtCollectionWrapper> {

		/**
		 * Fetches all query results in the background.
		 */
		@Override
		protected OsmtCollectionWrapper doInBackground(String... query) {
			// TODO add elementsFilter (poi, way, areas)
			OsmtCollectionWrapper wrapper = new OsmtCollectionWrapper();

			if (filterThemes) {
				wrapper.themeColl = osmtData.fetchAllThemesThatMatches(query[0], elementFilter);

			}
			if (filterBundles) {
				wrapper.bundleColl = osmtData.fetchAllBundlesThatMatches(query[0],
						elementFilter);
			}

			if (filterFeatures) {
				Log.d(Constants.TAG, "filter features" + query[0]);
				wrapper.featureColl = osmtData.fetchAllFeaturesThatMatches(query[0],
						elementFilter);
			}
			return wrapper;
		}

		/**
		 * Adds query results to the adapter.
		 */
		@Override
		protected void onPostExecute(OsmtCollectionWrapper result) {
			// add themes
			if (result.themeColl != null && result.themeColl.size() > 0) {
				(TagSearchAdapter.this).add(new TagListItem(themeTitle));
				for (OsmtNodeRecord record : result.themeColl)
					TagSearchAdapter.this.add(new TagListItem(record, ItemType.THEME_ITEM));
			}
			// add bundle
			if (result.bundleColl != null && result.bundleColl.size() > 0) {
				(TagSearchAdapter.this).add(new TagListItem(bundleTitle));
				for (OsmtNodeRecord record : result.bundleColl)
					TagSearchAdapter.this.add(new TagListItem(record, ItemType.BUNDLE_ITEM));
			}
			// add features
			if (result.featureColl != null && result.featureColl.size() > 0) {
				(TagSearchAdapter.this).add(new TagListItem(featureTitle));
				for (OsmtFeatureRecord record : result.featureColl)
					TagSearchAdapter.this.add(new TagListItem(record, ItemType.FEATURE_ITEM));
			}

		}

	}

	/**
	 * A wrapper class which contains three collections: the results in the
	 * feature table, bundle table and theme table. The wrapper class is
	 * necessary because the results of a AsyncTask have to be of one type.
	 */
	class OsmtCollectionWrapper {
		/**
		 * Collection of the results in the feature table.
		 */
		Collection<OsmtFeatureRecord> featureColl;
		/**
		 * Collection of the results in the bundle table.
		 */
		Collection<OsmtNodeRecord> bundleColl;
		/**
		 * Collection of the results in the theme table.
		 */
		Collection<OsmtNodeRecord> themeColl;

		/**
		 * Constructor.
		 */
		OsmtCollectionWrapper() {
			// nothing to do
		}
	}
}
